using System;
using UnityEngine;
using DanielSig.UnityExtensions;

namespace DanielSig.UnityExtensions
{
	public static class XRay
	{
		public static float DistanceToPoint(this Ray ray, Vector3 point)
		{
			return Mathf.Sqrt(ray.DistanceToPointSquared(point));
		}
		public static float DistanceToPointSquared(this Ray ray, Vector3 point)
		{
			return (Vector3.Cross(ray.direction, ray.origin - point).sqrMagnitude) / ray.direction.sqrMagnitude;
		}
		
		public static RaycastHit Blast(this Ray ray, float force)
		{
			return ray.Push(force, ForceMode.Force, false);
		}
		
		public static RaycastHit Blast(this Ray ray, float force, ForceMode mode)
		{
			return ray.Push(force, mode, false);
		}
		
		public static RaycastHit Push(this Ray ray, float force)
		{
			return ray.Push(force, ForceMode.Force, true);
		}
		public static RaycastHit Push(this Ray ray, float force, ForceMode mode)
		{
			return ray.Push(force, mode, true);
		}
		public static RaycastHit Push(this Ray ray, float force, ForceMode mode, bool direct)
		{
			RaycastHit info = ray.GetHit();
			if(info.transform)
			{
				Rigidbody body = info.transform.GetAbsRigidbody();
				if(body)
				{
					if(direct)
					{
						body.AddForce(ray.direction * force, mode);
					}
					else
					{
						body.AddForceAtPosition(ray.direction * force, info.point, mode);
					}
				}
			}
			return info;
		}
		
		public static RaycastHit PushDebug(this Ray ray, float force)
		{
			return ray.PushDebug(force, ForceMode.Force, true, 0);
		}
		public static RaycastHit PushDebug(this Ray ray, float force, ForceMode mode)
		{
			return ray.PushDebug(force, mode, true, 0);
		}
		public static RaycastHit PushDebug(this Ray ray, float force, ForceMode mode, bool direct)
		{
			return ray.PushDebug(force, mode, direct, 0);
		}
		
		
		public static RaycastHit PushDebug(this Ray ray, float force, float debugDuration)
		{
			return ray.PushDebug(force, ForceMode.Force, true, debugDuration);
		}
		public static RaycastHit PushDebug(this Ray ray, float force, ForceMode mode, float debugDuration)
		{
			return ray.PushDebug(force, mode, true, debugDuration);
		}
		public static RaycastHit PushDebug(this Ray ray, float force, ForceMode mode, bool direct, float debugDuration)
		{
			RaycastHit info = ray.Push(force, mode, direct);
			if(info.transform)
			{
				Debug.DrawLine(info.transform.position, info.transform.GetAbsRigidbody().transform.position, Color.yellow);
				if(info.transform.GetAbsRigidbody())
				{
					Debug.DrawLine(ray.origin, info.point, Color.green, debugDuration);
				}
				else
				{
					Debug.DrawLine(ray.origin, info.point, Color.gray, debugDuration);
				}
			}
			else
			{
				Debug.DrawRay(ray.origin, ray.direction * 3F, Color.red, debugDuration);
			}
			if(debugDuration == 0)
			{
				Debug.Break();
			}
			return info;
		}
		
		
		public static RaycastHit GetHit(this Ray ray)
		{
			return ray.GetHit(Mathf.Infinity);
		}
		public static RaycastHit GetHit(this Ray ray, int layerMask)
		{
			return ray.GetHit(Mathf.Infinity, layerMask);
		}
		public static RaycastHit GetHit(this Ray ray, float distance)
		{
			RaycastHit info;
			Physics.Raycast(ray, out info, distance);
			return info;
		}
		public static RaycastHit GetHit(this Ray ray, float distance, int layerMask)
		{
			RaycastHit info;
			Physics.Raycast(ray, out info, distance, layerMask);
			return info;
		}
		public static RaycastHit GetHit(this Ray ray, float distance, float radius)
		{
			RaycastHit info;
			Physics.SphereCast(ray, radius, out info, distance);
			return info;
		}
		public static RaycastHit GetHit(this Ray ray, float distance, float radius, int layerMask)
		{
			RaycastHit info;
			Physics.SphereCast(ray, radius, out info, distance, layerMask);
			return info;
		}
	}
}

